/**
 * Copyright (C) 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.inject.spi;

import static com.google.inject.internal.MoreTypes.getRawType;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.inject.ConfigurationException;
import com.google.inject.Inject;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.Annotations;
import com.google.inject.internal.Errors;
import com.google.inject.internal.ErrorsException;
import com.google.inject.internal.Nullability;
import com.google.inject.internal.util.Classes;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * A constructor, field or method that can receive injections. Typically this is a member with the
 * {@literal @}{@link Inject} annotation. For non-private, no argument constructors, the member may
 * omit the annotation.
 *
 * @author crazybob@google.com (Bob Lee)
 * @since 2.0
 */
public final class InjectionPoint {

    private static final Logger logger = Logger.getLogger(InjectionPoint.class.getName());

    private final boolean optional;
    private final Member member;
    private final TypeLiteral<?> declaringType;
    private final ImmutableList<Dependency<?>> dependencies;

    InjectionPoint(TypeLiteral<?> declaringType, Method method, boolean optional) {
        this.member = method;
        this.declaringType = declaringType;
        this.optional = optional;
        this.dependencies = forMember(method, declaringType, method.getParameterAnnotations());
    }

    InjectionPoint(TypeLiteral<?> declaringType, Constructor<?> constructor) {
        this.member = constructor;
        this.declaringType = declaringType;
        this.optional = false;
        this.dependencies = forMember(constructor, declaringType, constructor.getParameterAnnotations());
    }

    InjectionPoint(TypeLiteral<?> declaringType, Field field, boolean optional) {
        this.member = field;
        this.declaringType = declaringType;
        this.optional = optional;

        Annotation[] annotations = field.getAnnotations();

        Errors errors = new Errors(field);
        Key<?> key = null;
        try {
            key = Annotations.getKey(declaringType.getFieldType(field), field, annotations, errors);
        } catch (ConfigurationException e) {
            errors.merge(e.getErrorMessages());
        } catch (ErrorsException e) {
            errors.merge(e.getErrors());
        }
        errors.throwConfigurationExceptionIfErrorsExist();

        this.dependencies = ImmutableList.<Dependency<?>> of(newDependency(key, Nullability.allowsNull(annotations), -1));
    }

    private ImmutableList<Dependency<?>> forMember(Member member, TypeLiteral<?> type, Annotation[][] paramterAnnotations) {
        Errors errors = new Errors(member);
        Iterator<Annotation[]> annotationsIterator = Arrays.asList(paramterAnnotations).iterator();

        List<Dependency<?>> dependencies = Lists.newArrayList();
        int index = 0;

        for (TypeLiteral<?> parameterType : type.getParameterTypes(member)) {
            try {
                Annotation[] parameterAnnotations = annotationsIterator.next();
                Key<?> key = Annotations.getKey(parameterType, member, parameterAnnotations, errors);
                dependencies.add(newDependency(key, Nullability.allowsNull(parameterAnnotations), index));
                index++;
            } catch (ConfigurationException e) {
                errors.merge(e.getErrorMessages());
            } catch (ErrorsException e) {
                errors.merge(e.getErrors());
            }
        }

        errors.throwConfigurationExceptionIfErrorsExist();
        return ImmutableList.copyOf(dependencies);
    }

    // This metohd is necessary to create a Dependency<T> with proper generic type information
    private <T> Dependency<T> newDependency(Key<T> key, boolean allowsNull, int parameterIndex) {
        return new Dependency<T>(this, key, allowsNull, parameterIndex);
    }

    /**
     * Returns the injected constructor, field, or method.
     */
    public Member getMember() {
        // TODO: Don't expose the original member (which probably has setAccessible(true)).
        return member;
    }

    /**
     * Returns the dependencies for this injection point. If the injection point is for a method or
     * constructor, the dependencies will correspond to that member's parameters. Field injection
     * points always have a single dependency for the field itself.
     *
     * @return a possibly-empty list
     */
    public List<Dependency<?>> getDependencies() {
        return dependencies;
    }

    /**
     * Returns true if this injection point shall be skipped if the injector cannot resolve bindings
     * for all required dependencies. Both explicit bindings (as specified in a module), and implicit
     * bindings ({@literal @}{@link com.google.inject.ImplementedBy ImplementedBy}, default
     * constructors etc.) may be used to satisfy optional injection points.
     */
    public boolean isOptional() {
        return optional;
    }

    /**
     * Returns true if the element is annotated with {@literal @}{@link Toolable}.
     * 
     * @since 3.0
     */
    public boolean isToolable() {
        return ((AnnotatedElement) member).isAnnotationPresent(Toolable.class);
    }

    /**
     * Returns the generic type that defines this injection point. If the member exists on a
     * parameterized type, the result will include more type information than the member's {@link
     * Member#getDeclaringClass() raw declaring class}.
     * 
     * @since 3.0
     */
    public TypeLiteral<?> getDeclaringType() {
        return declaringType;
    }

    @Override
    public boolean equals(Object o) {
        return o instanceof InjectionPoint && member.equals(((InjectionPoint) o).member) && declaringType.equals(((InjectionPoint) o).declaringType);
    }

    @Override
    public int hashCode() {
        return member.hashCode() ^ declaringType.hashCode();
    }

    @Override
    public String toString() {
        return Classes.toString(member);
    }

    /**
     * Returns a new injection point for the specified constructor. If the declaring type of {@code
     * constructor} is parameterized (such as {@code List<T>}), prefer the overload that includes a
     * type literal.
     *
     * @param constructor any single constructor present on {@code type}.
     * 
     * @since 3.0
     */
    public static <T> InjectionPoint forConstructor(Constructor<T> constructor) {
        return new InjectionPoint(TypeLiteral.get(constructor.getDeclaringClass()), constructor);
    }

    /**
     * Returns a new injection point for the specified constructor of {@code type}.
     *
     * @param constructor any single constructor present on {@code type}.
     * @param type the concrete type that defines {@code constructor}.
     * 
     * @since 3.0
     */
    public static <T> InjectionPoint forConstructor(Constructor<T> constructor, TypeLiteral<? extends T> type) {
        if (type.getRawType() != constructor.getDeclaringClass()) {
            new Errors(type).constructorNotDefinedByType(constructor, type).throwConfigurationExceptionIfErrorsExist();
        }

        return new InjectionPoint(type, constructor);
    }

    /**
     * 
     * 返回一个依赖注入点
     * Returns a new injection point for the injectable constructor of {@code type}.
     *
     * @param type a concrete type with exactly one constructor annotated {@literal @}{@link Inject},
     *     or a no-arguments constructor that is not private.
     * @throws ConfigurationException if there is no injectable constructor, more than one injectable
     *     constructor, or if parameters of the injectable constructor are malformed, such as a
     *     parameter with multiple binding annotations.
     */
    public static InjectionPoint forConstructorOf(TypeLiteral<?> type) {
        Class<?> rawType = getRawType(type.getType());
        Errors errors = new Errors(rawType);

        Constructor<?> injectableConstructor = null;
        for (Constructor<?> constructor : rawType.getDeclaredConstructors()) {

            boolean optional;
            Inject guiceInject = constructor.getAnnotation(Inject.class);
            if (guiceInject == null) {
                javax.inject.Inject javaxInject = constructor.getAnnotation(javax.inject.Inject.class);
                if (javaxInject == null) {
                    continue;
                }
                optional = false;
            } else {
                optional = guiceInject.optional();
            }

            if (optional) {
                errors.optionalConstructor(constructor);
            }

            if (injectableConstructor != null) {
                errors.tooManyConstructors(rawType);
            }

            injectableConstructor = constructor;
            checkForMisplacedBindingAnnotations(injectableConstructor, errors);
        }

        errors.throwConfigurationExceptionIfErrorsExist();

        if (injectableConstructor != null) {
            return new InjectionPoint(type, injectableConstructor);
        }

        //此处是做自动引入   为啥不是用Module中已经生成的对象呢
        // If no annotated constructor is found, look for a no-arg constructor instead.
        try {
            Constructor<?> noArgConstructor = rawType.getDeclaredConstructor();

            // Disallow private constructors on non-private classes (unless they have @Inject)
            if (Modifier.isPrivate(noArgConstructor.getModifiers()) && !Modifier.isPrivate(rawType.getModifiers())) {
                errors.missingConstructor(rawType);
                throw new ConfigurationException(errors.getMessages());
            }

            checkForMisplacedBindingAnnotations(noArgConstructor, errors);
            return new InjectionPoint(type, noArgConstructor);
        } catch (NoSuchMethodException e) {
            errors.missingConstructor(rawType);
            throw new ConfigurationException(errors.getMessages());
        }
    }

    /**
     * Returns a new injection point for the injectable constructor of {@code type}.
     *
     * @param type a concrete type with exactly one constructor annotated {@literal @}{@link Inject},
     *     or a no-arguments constructor that is not private.
     * @throws ConfigurationException if there is no injectable constructor, more than one injectable
     *     constructor, or if parameters of the injectable constructor are malformed, such as a
     *     parameter with multiple binding annotations.
     */
    public static InjectionPoint forConstructorOf(Class<?> type) {
        return forConstructorOf(TypeLiteral.get(type));
    }

    /**
     * Returns a new injection point for the specified method of {@code type}.
     * This is useful for extensions that need to build dependency graphs from
     * arbitrary methods.
     *
     * @param method any single method present on {@code type}.
     * @param type the concrete type that defines {@code method}.
     *
     * @since 4.0
     */
    public static <T> InjectionPoint forMethod(Method method, TypeLiteral<T> type) {
        return new InjectionPoint(type, method, false);
    }

    /**
     * Returns all static method and field injection points on {@code type}.
     *
     * @return a possibly empty set of injection points. The set has a specified iteration order. All
     *      fields are returned and then all methods. Within the fields, supertype fields are returned
     *      before subtype fields. Similarly, supertype methods are returned before subtype methods.
     * @throws ConfigurationException if there is a malformed injection point on {@code type}, such as
     *      a field with multiple binding annotations. The exception's {@link
     *      ConfigurationException#getPartialValue() partial value} is a {@code Set<InjectionPoint>}
     *      of the valid injection points.
     */
    public static Set<InjectionPoint> forStaticMethodsAndFields(TypeLiteral<?> type) {
        Errors errors = new Errors();

        Set<InjectionPoint> result;

        if (type.getRawType().isInterface()) {
            errors.staticInjectionOnInterface(type.getRawType());
            result = null;
        } else {
            result = getInjectionPoints(type, true, errors);
        }

        if (errors.hasErrors()) {
            throw new ConfigurationException(errors.getMessages()).withPartialValue(result);
        }
        return result;
    }

    /**
     * Returns all static method and field injection points on {@code type}.
     *
     * @return a possibly empty set of injection points. The set has a specified iteration order. All
     *      fields are returned and then all methods. Within the fields, supertype fields are returned
     *      before subtype fields. Similarly, supertype methods are returned before subtype methods.
     * @throws ConfigurationException if there is a malformed injection point on {@code type}, such as
     *      a field with multiple binding annotations. The exception's {@link
     *      ConfigurationException#getPartialValue() partial value} is a {@code Set<InjectionPoint>}
     *      of the valid injection points.
     */
    public static Set<InjectionPoint> forStaticMethodsAndFields(Class<?> type) {
        return forStaticMethodsAndFields(TypeLiteral.get(type));
    }

    /**
     * Returns all instance method and field injection points on {@code type}.
     *
     * @return a possibly empty set of injection points. The set has a specified iteration order. All
     *      fields are returned and then all methods. Within the fields, supertype fields are returned
     *      before subtype fields. Similarly, supertype methods are returned before subtype methods.
     * @throws ConfigurationException if there is a malformed injection point on {@code type}, such as
     *      a field with multiple binding annotations. The exception's {@link
     *      ConfigurationException#getPartialValue() partial value} is a {@code Set<InjectionPoint>}
     *      of the valid injection points.
     */
    public static Set<InjectionPoint> forInstanceMethodsAndFields(TypeLiteral<?> type) {
        Errors errors = new Errors();
        Set<InjectionPoint> result = getInjectionPoints(type, false, errors);
        if (errors.hasErrors()) {
            throw new ConfigurationException(errors.getMessages()).withPartialValue(result);
        }
        return result;
    }

    /**
     * Returns all instance method and field injection points on {@code type}.
     *
     * @return a possibly empty set of injection points. The set has a specified iteration order. All
     *      fields are returned and then all methods. Within the fields, supertype fields are returned
     *      before subtype fields. Similarly, supertype methods are returned before subtype methods.
     * @throws ConfigurationException if there is a malformed injection point on {@code type}, such as
     *      a field with multiple binding annotations. The exception's {@link
     *      ConfigurationException#getPartialValue() partial value} is a {@code Set<InjectionPoint>}
     *      of the valid injection points.
     */
    public static Set<InjectionPoint> forInstanceMethodsAndFields(Class<?> type) {
        return forInstanceMethodsAndFields(TypeLiteral.get(type));
    }

    /**
     * Returns true if the binding annotation is in the wrong place.
     */
    private static boolean checkForMisplacedBindingAnnotations(Member member, Errors errors) {
        Annotation misplacedBindingAnnotation = Annotations.findBindingAnnotation(errors, member, ((AnnotatedElement) member).getAnnotations());
        if (misplacedBindingAnnotation == null) {
            return false;
        }

        // don't warn about misplaced binding annotations on methods when there's a field with the same
        // name. In Scala, fields always get accessor methods (that we need to ignore). See bug 242.
        if (member instanceof Method) {
            try {
                if (member.getDeclaringClass().getDeclaredField(member.getName()) != null) {
                    return false;
                }
            } catch (NoSuchFieldException ignore) {
            }
        }

        errors.misplacedBindingAnnotation(member, misplacedBindingAnnotation);
        return true;
    }

    /**
     * Node in the doubly-linked list of injectable members (fields and methods).
     */
    static abstract class InjectableMember {
        final TypeLiteral<?> declaringType;
        final boolean optional;
        final boolean jsr330;
        InjectableMember previous;
        InjectableMember next;

        InjectableMember(TypeLiteral<?> declaringType, Annotation atInject) {
            this.declaringType = declaringType;

            if (atInject.annotationType() == javax.inject.Inject.class) {
                optional = false;
                jsr330 = true;
                return;
            }

            jsr330 = false;
            optional = ((Inject) atInject).optional();
        }

        abstract InjectionPoint toInjectionPoint();
    }

    static class InjectableField extends InjectableMember {
        final Field field;

        InjectableField(TypeLiteral<?> declaringType, Field field, Annotation atInject) {
            super(declaringType, atInject);
            this.field = field;
        }

        @Override
        InjectionPoint toInjectionPoint() {
            return new InjectionPoint(declaringType, field, optional);
        }
    }

    static class InjectableMethod extends InjectableMember {
        final Method method;
        /**
         * true if this method overrode a method that was annotated
         * with com.google.inject.Inject.  used to allow different
         * override behavior for guice inject vs javax.inject.Inject
         */
        boolean overrodeGuiceInject;

        InjectableMethod(TypeLiteral<?> declaringType, Method method, Annotation atInject) {
            super(declaringType, atInject);
            this.method = method;
        }

        @Override
        InjectionPoint toInjectionPoint() {
            return new InjectionPoint(declaringType, method, optional);
        }

        public boolean isFinal() {
            return Modifier.isFinal(method.getModifiers());
        }
    }

    static Annotation getAtInject(AnnotatedElement member) {
        Annotation a = member.getAnnotation(javax.inject.Inject.class);
        return a == null ? member.getAnnotation(Inject.class) : a;
    }

    /**
     * Linked list of injectable members.
     */
    static class InjectableMembers {
        InjectableMember head;
        InjectableMember tail;

        void add(InjectableMember member) {
            if (head == null) {
                head = tail = member;
            } else {
                member.previous = tail;
                tail.next = member;
                tail = member;
            }
        }

        void remove(InjectableMember member) {
            if (member.previous != null) {
                member.previous.next = member.next;
            }
            if (member.next != null) {
                member.next.previous = member.previous;
            }
            if (head == member) {
                head = member.next;
            }
            if (tail == member) {
                tail = member.previous;
            }
        }

        boolean isEmpty() {
            return head == null;
        }
    }

    /** Position in type hierarchy. */
    enum Position {
        TOP, // No need to check for overridden methods
        MIDDLE, BOTTOM // Methods won't be overridden
    }

    /**
     * Keeps track of injectable methods so we can remove methods that get overridden in O(1) time.
     * Uses our position in the type hierarchy to perform optimizations.
     */
    static class OverrideIndex {
        final InjectableMembers injectableMembers;
        Map<Signature, List<InjectableMethod>> bySignature;
        Position position = Position.TOP;

        OverrideIndex(InjectableMembers injectableMembers) {
            this.injectableMembers = injectableMembers;
        }

        /* Caches the signature for the last method. */
        Method lastMethod;
        Signature lastSignature;

        /**
         * Removes a method overridden by the given method, if present. In order to
         * remain backwards compatible with prior Guice versions, this will *not*
         * remove overridden methods if 'alwaysRemove' is false and the overridden
         * signature was annotated with a com.google.inject.Inject.
         * 
         * @param method
         *          The method used to determine what is overridden and should be
         *          removed.
         * @param alwaysRemove
         *          true if overridden methods should be removed even if they were
         *          guice @Inject
         * @param injectableMethod
         *          if this method overrode any guice @Inject methods,
         *          {@link InjectableMethod#overrodeGuiceInject} is set to true
         */
        boolean removeIfOverriddenBy(Method method, boolean alwaysRemove, InjectableMethod injectableMethod) {
            if (position == Position.TOP) {
                // If we're at the top of the hierarchy, there's nothing to override.
                return false;
            }

            if (bySignature == null) {
                // We encountered a method in a subclass. Time to index the
                // methods in the parent class.
                bySignature = new HashMap<Signature, List<InjectableMethod>>();
                for (InjectableMember member = injectableMembers.head; member != null; member = member.next) {
                    if (!(member instanceof InjectableMethod))
                        continue;
                    InjectableMethod im = (InjectableMethod) member;
                    if (im.isFinal())
                        continue;
                    List<InjectableMethod> methods = new ArrayList<InjectableMethod>();
                    methods.add(im);
                    bySignature.put(new Signature(im.method), methods);
                }
            }

            lastMethod = method;
            Signature signature = lastSignature = new Signature(method);
            List<InjectableMethod> methods = bySignature.get(signature);
            boolean removed = false;
            if (methods != null) {
                for (Iterator<InjectableMethod> iterator = methods.iterator(); iterator.hasNext();) {
                    InjectableMethod possiblyOverridden = iterator.next();
                    if (overrides(method, possiblyOverridden.method)) {
                        boolean wasGuiceInject = !possiblyOverridden.jsr330 || possiblyOverridden.overrodeGuiceInject;
                        if (injectableMethod != null) {
                            injectableMethod.overrodeGuiceInject = wasGuiceInject;
                        }
                        // Only actually remove the methods if we want to force
                        // remove or if the signature never specified @com.google.inject.Inject
                        // somewhere.
                        if (alwaysRemove || !wasGuiceInject) {
                            removed = true;
                            iterator.remove();
                            injectableMembers.remove(possiblyOverridden);
                        }
                    }
                }
            }
            return removed;
        }

        /**
         * Adds the given method to the list of injection points. Keeps track of it in this index
         * in case it gets overridden.
         */
        void add(InjectableMethod injectableMethod) {
            injectableMembers.add(injectableMethod);
            if (position == Position.BOTTOM || injectableMethod.isFinal()) {
                // This method can't be overridden, so there's no need to index it.
                return;
            }
            if (bySignature != null) {
                // Try to reuse the signature we created during removal
                Signature signature = injectableMethod.method == lastMethod ? lastSignature : new Signature(injectableMethod.method);
                List<InjectableMethod> methods = bySignature.get(signature);
                if (methods == null) {
                    methods = new ArrayList<InjectableMethod>();
                    bySignature.put(signature, methods);
                }
                methods.add(injectableMethod);
            }
        }
    }

    /**
     * Returns an ordered, immutable set of injection points for the given type. Members in
     * superclasses come before members in subclasses. Within a class, fields come before methods.
     * Overridden methods are filtered out.
     *
     * @param statics true is this method should return static members, false for instance members
     * @param errors used to record errors
     */
    private static Set<InjectionPoint> getInjectionPoints(final TypeLiteral<?> type, boolean statics, Errors errors) {
        InjectableMembers injectableMembers = new InjectableMembers();
        OverrideIndex overrideIndex = null;

        List<TypeLiteral<?>> hierarchy = hierarchyFor(type);
        int topIndex = hierarchy.size() - 1;
        for (int i = topIndex; i >= 0; i--) {
            if (overrideIndex != null && i < topIndex) {
                // Knowing the position within the hierarchy helps us make optimizations.
                if (i == 0) {
                    overrideIndex.position = Position.BOTTOM;
                } else {
                    overrideIndex.position = Position.MIDDLE;
                }
            }

            TypeLiteral<?> current = hierarchy.get(i);

            for (Field field : current.getRawType().getDeclaredFields()) {
                if (Modifier.isStatic(field.getModifiers()) == statics) {
                    Annotation atInject = getAtInject(field);
                    if (atInject != null) {
                        InjectableField injectableField = new InjectableField(current, field, atInject);
                        if (injectableField.jsr330 && Modifier.isFinal(field.getModifiers())) {
                            errors.cannotInjectFinalField(field);
                        }
                        injectableMembers.add(injectableField);
                    }
                }
            }

            for (Method method : current.getRawType().getDeclaredMethods()) {
                if (isEligibleForInjection(method, statics)) {
                    Annotation atInject = getAtInject(method);
                    if (atInject != null) {
                        InjectableMethod injectableMethod = new InjectableMethod(current, method, atInject);
                        if (checkForMisplacedBindingAnnotations(method, errors) || !isValidMethod(injectableMethod, errors)) {
                            if (overrideIndex != null) {
                                boolean removed = overrideIndex.removeIfOverriddenBy(method, false, injectableMethod);
                                if (removed) {
                                    logger.log(Level.WARNING, "Method: {0} is not a valid injectable method (" + "because it either has misplaced binding annotations " + "or specifies type parameters) but is overriding a method that is valid. " + "Because it is not valid, the method will not be injected. " + "To fix this, make the method a valid injectable method.", method);
                                }
                            }
                            continue;
                        }
                        if (statics) {
                            injectableMembers.add(injectableMethod);
                        } else {
                            if (overrideIndex == null) {
                                /*
                                 * Creating the override index lazily means that the first type in the hierarchy
                                 * with injectable methods (not necessarily the top most type) will be treated as
                                 * the TOP position and will enjoy the same optimizations (no checks for overridden
                                 * methods, etc.).
                                 */
                                overrideIndex = new OverrideIndex(injectableMembers);
                            } else {
                                // Forcibly remove the overriden method, otherwise we'll inject
                                // it twice.
                                overrideIndex.removeIfOverriddenBy(method, true, injectableMethod);
                            }
                            overrideIndex.add(injectableMethod);
                        }
                    } else {
                        if (overrideIndex != null) {
                            boolean removed = overrideIndex.removeIfOverriddenBy(method, false, null);
                            if (removed) {
                                logger.log(Level.WARNING, "Method: {0} is not annotated with @Inject but " + "is overriding a method that is annotated with @javax.inject.Inject.  Because " + "it is not annotated with @Inject, the method will not be injected. " + "To fix this, annotate the method with @Inject.", method);
                            }
                        }
                    }
                }
            }
        }

        if (injectableMembers.isEmpty()) {
            return Collections.emptySet();
        }

        ImmutableSet.Builder<InjectionPoint> builder = ImmutableSet.builder();
        for (InjectableMember im = injectableMembers.head; im != null; im = im.next) {
            try {
                builder.add(im.toInjectionPoint());
            } catch (ConfigurationException ignorable) {
                if (!im.optional) {
                    errors.merge(ignorable.getErrorMessages());
                }
            }
        }
        return builder.build();
    }

    /** 
     * Returns true if the method is eligible to be injected.  This is different than
     * {@link #isValidMethod}, because ineligibility will not drop a method
     * from being injected if a superclass was eligible & valid. 
     * Bridge & synthetic methods are excluded from eligibility for two reasons:
     * 
     * <p>Prior to Java8, javac would generate these methods in subclasses without
     * annotations, which means this would accidentally stop injecting a method
     * annotated with {@link javax.inject.Inject}, since the spec says to stop
     * injecting if a subclass isn't annotated with it.
     * 
     * <p>Starting at Java8, javac copies the annotations to the generated subclass
     * method, except it leaves out the generic types.  If this considered it a valid
     * injectable method, this would eject the parent's overridden method that had the
     * proper generic types, and would use invalid injectable parameters as a result.
     * 
     * <p>The fix for both is simply to ignore these synthetic bridge methods.
     */
    private static boolean isEligibleForInjection(Method method, boolean statics) {
        return Modifier.isStatic(method.getModifiers()) == statics && !method.isBridge() && !method.isSynthetic();
    }

    private static boolean isValidMethod(InjectableMethod injectableMethod, Errors errors) {
        boolean result = true;
        if (injectableMethod.jsr330) {
            Method method = injectableMethod.method;
            if (Modifier.isAbstract(method.getModifiers())) {
                errors.cannotInjectAbstractMethod(method);
                result = false;
            }
            if (method.getTypeParameters().length > 0) {
                errors.cannotInjectMethodWithTypeParameters(method);
                result = false;
            }
        }
        return result;
    }

    private static List<TypeLiteral<?>> hierarchyFor(TypeLiteral<?> type) {
        List<TypeLiteral<?>> hierarchy = new ArrayList<TypeLiteral<?>>();
        TypeLiteral<?> current = type;
        while (current.getRawType() != Object.class) {
            hierarchy.add(current);
            current = current.getSupertype(current.getRawType().getSuperclass());
        }
        return hierarchy;
    }

    /**
     * Returns true if a overrides b. Assumes signatures of a and b are the same and a's declaring
     * class is a subclass of b's declaring class.
     */
    private static boolean overrides(Method a, Method b) {
        // See JLS section 8.4.8.1
        int modifiers = b.getModifiers();
        if (Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers)) {
            return true;
        }
        if (Modifier.isPrivate(modifiers)) {
            return false;
        }
        // b must be package-private
        return a.getDeclaringClass().getPackage().equals(b.getDeclaringClass().getPackage());
    }

    /**
     * A method signature. Used to handle method overridding.
     */
    static class Signature {

        final String name;
        final Class[] parameterTypes;
        final int hash;

        Signature(Method method) {
            this.name = method.getName();
            this.parameterTypes = method.getParameterTypes();

            int h = name.hashCode();
            h = h * 31 + parameterTypes.length;
            for (Class parameterType : parameterTypes) {
                h = h * 31 + parameterType.hashCode();
            }
            this.hash = h;
        }

        @Override
        public int hashCode() {
            return this.hash;
        }

        @Override
        public boolean equals(Object o) {
            if (!(o instanceof Signature)) {
                return false;
            }

            Signature other = (Signature) o;
            if (!name.equals(other.name)) {
                return false;
            }

            if (parameterTypes.length != other.parameterTypes.length) {
                return false;
            }

            for (int i = 0; i < parameterTypes.length; i++) {
                if (parameterTypes[i] != other.parameterTypes[i]) {
                    return false;
                }
            }

            return true;
        }
    }
}
